import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map.Entry;

/**
 * Query
 * reprents one line in the CorpusQueries-jEdit4.3-AfterSplitStopStem.txt
 */

/**
 * @author Kaidi & Xiaoran
 * 
 */
public class Query {
	
	
	
	public int queryID;
	
	private String queryStr = null;
	private String[] queryWordsArr = null;
	// query vector
	private HashMap<String, Integer> queryVector = null;
	private HashMap<String, Double> idfMap = null;

	// tfLinkList: contains all the tf of one doc
	private LinkedList<HashMap<String, Double>> tfLinkList = null;

	public LinkedList<Double> simValLinkList = null;// linked list

	// from queryID --> list of gold set
//	public LinkedList<String> goldSetMethdList = null;
	public LinkedList<GoldSetMethod> goldSetMethodList = null;
	public int goldSetBestRank = Integer.MAX_VALUE;
	
	private double maxSimValue = 0.0;

	private int maxSimValueLineNumber;
	
	public Double[] simValArraySorted = null;

	/**
	 * Constructor compute everything in the Constructor
	 * 
	 * @param queryID
	 * @param queryStr
	 * @param tfll
	 * @param idf
	 */
	public Query(int qryID, String qryStr,
			final LinkedList<HashMap<String, Double>> tfll,
			final HashMap<String, Double> idf) {
		queryID = qryID;
		queryStr = qryStr;
		// get query terms
		queryWordsArr = queryStr.split(" ");
		// prepare the query vector
		queryVector = new HashMap<String, Integer>();
		// prepare the sim matrix for every docs
		simValLinkList = new LinkedList<Double>();
		// tf should be calculated already

		tfLinkList = tfll;
		// idf should be calculated already
		idfMap = idf;
		
//		goldSetMethdList = getGoldSetList(queryID);
		goldSetMethodList = getGoldSetMethodList(queryID);

	}

	public void calculateResult(LinkedList<String> methodIDList){
				
		// build the query vector according to query terms
		for (String a : queryWordsArr) {
			if (queryVector.keySet().contains(a)) {
				queryVector.put(a, queryVector.get(a) + 1);
			} else {
				queryVector.put(a, 1);
			}
		}

		// get all sim values here
		// go through every docs in tfLinkList
		for (HashMap<String, Double> tfDocVector : tfLinkList) {

			// temp map updated: compute the wi_j for one term
			HashMap<String, Double> updated = new HashMap<String, Double>();
			for (Entry<String, Double> entry : tfDocVector.entrySet()) {
				String aWord = entry.getKey();
				double x = entry.getValue() * idfMap.get(aWord);
				updated.put(aWord, x);
			}

			//System.out.println("-----------line tf-idf----------");
			//Main.printMap(updated);
			
			// get similarity for one doc
			double sim = sim(queryVector, updated);

			simValLinkList.addLast(sim);
		}
		maxSimValue = Collections.max(simValLinkList);
		maxSimValueLineNumber = simValLinkList.indexOf(maxSimValue) + 1; 
		
		if(IDebug.ENANLE){
			System.out.println(queryID + " --- ");
			System.out.println("query line number: " + queryID);
			System.out.println("best match is in line: " + maxSimValueLineNumber);
		}
		//compute all the information for goldset methods
		simValArraySorted = simValLinkList.toArray(new Double[simValLinkList.size()]);
		Arrays.sort(simValArraySorted, Collections.reverseOrder());
		for(GoldSetMethod aMethod: goldSetMethodList)
		{
			int idxOfMethod = methodIDList.indexOf(aMethod.MethodID);//careful about offset
			double d = 0.0;
			if(idxOfMethod >=0)
			{
				d=simValLinkList.get(idxOfMethod);
				aMethod.LineNumber = idxOfMethod+1;
				// compute the ranks for the method
				aMethod.Rank = Arrays.asList(simValArraySorted).indexOf(d) +1;//careful about offset
				if(aMethod.Rank< goldSetBestRank)
				{
					goldSetBestRank = aMethod.Rank; 
				}
			}
		}
		
		// here output all results required by the final report
	}
	
	public static LinkedList<String> getGoldSetList(int queryID){
		
		
		LinkedList<String> list = new LinkedList<String>();
		
		File qryFileIn;
		FileInputStream fis = null;
		BufferedReader in = null;
		try {
			File dir = new File(".");
			String path = dir.getCanonicalPath() + File.separator;
			
			if(IDebug.ENANLE)
			{	System.out.println(path + "jEdit4.3/jEdit4.3GoldSets/GoldSet"+queryID+".txt");}
			
			qryFileIn = new File(path + "jEdit4.3/jEdit4.3GoldSets/GoldSet"+queryID+".txt");
			
			fis = new FileInputStream(qryFileIn);
			in = new BufferedReader(new InputStreamReader(fis));

			String aLine = null;
			// read line by line
			while ((aLine = in.readLine()) != null) {
				list.add(aLine.trim());
				//System.out.println(aLine);
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return list;
	}
	
	/**
	 * create list of GoldSetMethod
	 * @param queryID
	 * @return
	 */
	public LinkedList<GoldSetMethod> getGoldSetMethodList(int queryID){
		
		
		LinkedList<GoldSetMethod> list = new LinkedList<GoldSetMethod>();
		
		File qryFileIn;
		FileInputStream fis = null;
		BufferedReader in = null;
		try {
			File dir = new File(".");
			String path = dir.getCanonicalPath() + File.separator;
			
			if(IDebug.ENANLE)
			{	System.out.println(path + "jEdit4.3/jEdit4.3GoldSets/GoldSet"+queryID+".txt");}
			
			qryFileIn = new File(path + "jEdit4.3/jEdit4.3GoldSets/GoldSet"+queryID+".txt");
			
			fis = new FileInputStream(qryFileIn);
			in = new BufferedReader(new InputStreamReader(fis));

			String aLine = null;
			// read line by line
			while ((aLine = in.readLine()) != null) {
				String methodID = aLine.trim();
				GoldSetMethod gsm = new GoldSetMethod(methodID);
				list.add(gsm);
//				list.add();
				//System.out.println(aLine);
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return list;
	}
	
	/**
	 * compute the similarity between the query and all the docs
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public static double sim(final HashMap<String, Integer> a,
			final HashMap<String, Double> b) {

		double top = 0;
		int p1 = 0;
		for (Entry<String, Integer> entry : a.entrySet()) {
			String aWordinA = entry.getKey();
			Integer aValue = entry.getValue();
			if (b.keySet().contains(aWordinA)) {
				top = top + aValue * b.get(aWordinA);
			}
			p1 = p1 + aValue * aValue;
		}

		// p2
		double p2 = 0;
		for (Entry<String, Double> entry : b.entrySet()) {
			double bValue = entry.getValue();
			p2 = p2 + bValue * bValue;
		}

		// System.out.println("------" + top + " --- " + p1 + "   -----" + p2);

		return top / (Math.sqrt(p1) * Math.sqrt(p2));
	}

	/**
	 * print sim values in console
	 */
	public void PrintSimValues() {
		System.out.println("query:" + queryStr);
		for (double dSim : simValLinkList) {
			System.out.println("query: " + dSim);
		}
	}

	/**
	 * 20130926 Kaidi add: output 1 query result into tmp file
	 * 
	 * @param mp
	 * @param queryID
	 */
	public void printInfo2File(String filePath) {
		BufferedWriter out = null;
		try {
			out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(filePath, false)));
			String content = "QueryID: " + queryID + "\n";
			content += "Query:\n" + queryStr + "\n";
			content += "MaxSim: " + maxSimValue + " MaxSimDoc: "
					+ maxSimValueLineNumber + "\nSims:\n";

			int i = 1;
			for (double sim : simValLinkList) {
				content += i + ":" + sim + "\r";
				i++;
			}
			out.write(content);

		}
		// catch (FileNotFoundException e)
		// {
		// e.printStackTrace();
		// }
		catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	public void print2File(String filePath) {
		BufferedWriter out = null;
		try {
			out = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(filePath, false)));
			String content = "QueryID: " + queryID + "\n";
			content += "Query:\n" + queryStr + "\n";
			content += "MaxSim: " + maxSimValue + " MaxSimDoc: "
					+ maxSimValueLineNumber + "\nSims:\n";

			int i = 1;
			for (double sim : simValLinkList) {
				content += i + ":" + sim + "\r";
				i++;
			}
			out.write(content);

		}
		// catch (FileNotFoundException e)
		// {
		// e.printStackTrace();
		// }
		catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

}
